1 module hunt.templates.renderer;
2 
3 import std.string;
4 import std.file;
5 import std.path;
6 import std.conv;
7 import std.variant;
8 import std.json;
9 import std.stdio;
10 import std.uni;
11 import std.functional;
12 
13 import hunt.templates.rule;
14 import hunt.templates.element;
15 import hunt.templates.match;
16 import hunt.templates.ast;
17 import hunt.templates.util;
18 
19 class Renderer
20 {
21 public:
22     this()
23     {
24     }
25 
26     bool execCmp(T)(T a, T b, Function func)
27     {
28         //writeln("--------exec cmp : ", func);
29         switch (func)
30         {
31         case Function.Equal:
32             {
33                 return binaryFun!("a == b")(a, b);
34             }
35         case Function.Greater:
36             {
37                 return binaryFun!("a > b")(a, b);
38             }
39         case Function.Less:
40             {
41                 return binaryFun!("a < b")(a, b);
42             }
43         case Function.GreaterEqual:
44             {
45                 return binaryFun!("a >= b")(a, b);
46             }
47         case Function.LessEqual:
48             {
49                 return binaryFun!("a <= b")(a, b);
50             }
51         case Function.Different:
52             {
53                 return binaryFun!("a != b")(a, b);
54             }
55         default:
56             {
57                 return false;
58             }
59         }
60     }
61 
62     bool cmp(JSONValue a, JSONValue b, Function func)
63     {
64         //writeln("--------cmp : ", func, " a type :", a.type);
65         if (a.type != b.type)
66             return false;
67         if (a.type == JSONType.object || a.type == JSONType.array)
68             return false;
69         else if (a.type == JSONType..string)
70         {
71             return execCmp!string(a.str, b.str, func);
72         }
73         else if (a.type == JSONType.integer)
74         {
75             return execCmp!long(a.integer, b.integer, func);
76         }
77         else if (a.type == JSONType.true_)
78         {
79             return true;
80         }
81         else if (a.type == JSONType.false_)
82         {
83             return true;
84         }
85         else
86             return false;
87     }
88 
89     bool eval(JSONValue j)
90     {
91         if (j.type == JSONType.object || j.type == JSONType.array)
92             return true;
93         else if (j.type == JSONType..string)
94         {
95             return j.str.length > 0 ? true : false;
96         }
97         else if (j.type == JSONType.integer)
98         {
99             return j.integer > 0 ? true : false;
100         }
101         else if (j.type == JSONType.true_)
102         {
103             return true;
104         }
105         else if (j.type == JSONType.false_)
106         {
107             return false;
108         }
109         else
110             return true;
111     }
112 
113     T eval_expression(T = JSONValue)(ElementExpression element, ref JSONValue data)
114     {
115         auto var = eval_function!(T)(element, data);
116         return var;
117     }
118 
119     T eval_function(T = JSONValue)(ElementExpression element, ref JSONValue data)
120     {
121         T result;
122         //writeln("------element.func---- :", element.func);
123         switch (element.func)
124         {
125 
126         case Function.Upper:
127             {
128                 auto res = eval_expression(element.args[0], data);
129                 if (res.type == JSONType..string)
130                     result = toUpper(res.str);
131                 else
132                     result = toUpper(res.toString);
133                 return result;
134             }
135         case Function.Lower:
136             {
137                 auto res = eval_expression(element.args[0], data);
138                 if (res.type == JSONType..string)
139                     result = toLower(res.str);
140                 else
141                     result = toLower(res.toString);
142                 return result;
143             }
144         case Function.Equal:
145         case Function.Greater:
146         case Function.Less:
147         case Function.GreaterEqual:
148         case Function.LessEqual:
149         case Function.Different:
150             {
151                 result = cmp(eval_expression(element.args[0], data),
152                         eval_expression(element.args[1], data), element.func);
153                 return result;
154             }
155         case Function.ReadJson:
156             {
157                 try
158                 {
159                     //writeln("--read * json --:", element.command);
160                     if (element.command.length > 0 && element.command in data)
161                         result = data[element.command];
162                     else
163                     {
164                         auto cmds = split(element.command, ".");
165                         if (cmds.length > 1)
166                         {
167                             if (cmds.length == 2)
168                             {
169                                 if (cmds[0] in data)
170                                 {
171                                     if (Util.is_num(cmds[1]))
172                                     {
173                                         auto idx = to!int(cmds[1]);
174 
175                                         result = data[cmds[0]][idx];
176                                     }
177                                     else if (cmds[1] in data[cmds[0]])
178                                         result = data[cmds[0]][cmds[1]];
179                                 }
180 
181                             }
182                         }
183                         else
184                             result = element.command;
185                     }
186                 }
187                 catch (Exception e)
188                 {
189                     template_engine_throw("render_error",
190                             "variable '" ~ element.command ~ "' not found");
191                 }
192                 break;
193             }
194         case Function.Result:
195             {
196                 //writeln("--read result --:", element.result.toString);
197                 result = element.result;
198                 return result;
199             }
200         case Function.Default:
201             {
202                 //writeln("-----Function.Default----");
203                 try
204                 {
205                     return eval_expression!(T)(element.args[0], data);
206                 }
207                 catch (Exception e)
208                 {
209                     return eval_expression!(T)(element.args[1], data);
210                 }
211             }
212         default:
213             {
214                 template_engine_throw("render_error",
215                         "function '" ~ to!string(element.func) ~ "' not found");
216             }
217         }
218 
219         template_engine_throw("render_error", "unknown function in renderer: " ~ element.command);
220         return T();
221     }
222 
223     string render(ASTNode temp, ref JSONValue data)
224     {
225         string result = "";
226         //writeln("------temp.parsed_node.children-----: ",temp.parsed_node.children.length);
227         foreach (element; temp.parsed_node.children)
228         {
229             //writeln("------element.type-----: ",element.type);
230             switch (element.type)
231             {
232             case Type.String:
233                 {
234                     auto element_string = cast(ElementString)(element);
235                     result ~= element_string.text;
236                     break;
237                 }
238             case Type.Expression:
239                 {
240                     auto element_expression = cast(ElementExpression)(element);
241                     auto variable = eval_expression(element_expression, data);
242 
243                     // writeln("-----variable.type-------: ",variable.type);
244                     if (variable.type == JSONType..string)
245                     {
246                         result ~= variable.str;
247                     }
248                     else
249                     {
250 
251                         result ~= variable.toString;
252                     }
253                     break;
254                 }
255             case Type.Loop:
256                 {
257                     auto element_loop = cast(ElementLoop)(element);
258                     switch (element_loop.loop)
259                     {
260                     case Loop.ForListIn:
261                         {
262                             auto list = eval_expression(element_loop.list, data);
263                             //writeln("----list ----: ", list);
264                             if (list.type != JSONType.array)
265                             {
266                                 template_engine_throw("render_error",
267                                         list.toString ~ " is not an array");
268                             }
269                             foreach (size_t k, v; list)
270                             {
271                                 //writeln("v.type : ",v.type, " v.tostring :",v.toString);
272                                 JSONValue data_loop = parseJSON(data.toString);
273                                 data_loop["index"] = k;
274                                 data_loop[element_loop.value] = v;
275                                 result ~= render(new ASTNode(element_loop), data_loop);
276                             }
277                             break;
278                         }
279                     case Loop.ForMapIn:
280                         {
281                             auto map = eval_expression(element_loop.list, data);
282                             //writeln("----Loop type ----: ", map.type," map.toString : ",map.toString);
283                             if (map.type != JSONType.object)
284                             {
285                                 template_engine_throw("render_error",
286                                         map.toString ~ " is not an object");
287                             }
288                             foreach (string k, v; map)
289                             {
290                                 JSONValue data_loop = data;
291                                 data_loop[element_loop.key] = k;
292                                 data_loop[element_loop.value] = v;
293                                 result ~= render(new ASTNode(element_loop), data_loop);
294                             }
295                             break;
296                         }
297                     default:
298                         {
299                             break;
300                         }
301                     }
302 
303                     break;
304                 }
305             case Type.Condition:
306                 {
307                     auto element_condition = cast(ElementConditionContainer)(element);
308                     foreach (branch; element_condition.children)
309                     {
310                         auto element_branch = cast(ElementConditionBranch)(branch);
311                         //writeln("-----element_branch.type-------: ",element_branch.condition_type);
312                         auto flg = eval_expression(element_branch.condition, data);
313                         //writeln("-----flg.type-------: ",flg.type);
314                         if (element_branch.condition_type == Condition.Else || eval(flg))
315                         {
316                             result ~= render(new ASTNode(element_branch), data);
317                             break;
318                         }
319                     }
320                     break;
321                 }
322             default:
323                 {
324                     break;
325                 }
326             }
327         }
328         return result;
329     }
330 }